package com.linkgie.galaxyframework.utils.bean;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.ExtendedBeanInfoFactory;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessor;
import org.springframework.beans.PropertyAccessorUtils;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import com.linkgie.galaxyframework.utils.ApplicationException;
import com.linkgie.galaxyframework.utils.CollectionUtils;
import com.linkgie.galaxyframework.utils.SortedProperties;
import com.linkgie.galaxyframework.utils.StringConverter;
import com.linkgie.galaxyframework.utils.io.FileUtils;
import com.linkgie.galaxyframework.utils.io.PropertiesUtils;

/**
 * BeanUtils 定义了从 properties 文件到 pojo 对象的转换方法；
 * 
 * 用于对充当配置文件的 properties 文件到定义了配置信息的 POJO 的转换；
 * 
 * 支持 properties 的 key 到 POJO 的字段转换，支持层级的 key 的转换，例如： "user.name" 到 user 字段的对象的
 * name 属性；
 * 
 * @author haiq
 *
 */
public abstract class BeanUtils {

	public static final String PROPERTY_SEPERATOR = ".";

	public static final char PROPERTY_SEPERATOR_CHAR = '.';

	/**
	 * 显示引入实现类，避免其它依赖此包的组件在 shape 方式打包时面临失败风险；
	 */
	@SuppressWarnings("unused")
	private static final ExtendedBeanInfoFactory beanInfoFactory = new ExtendedBeanInfoFactory();

	private BeanUtils() {
	}

	/**
	 * 创建配置对象的实例，并且从指定的属性表中初始化对应的实例字段；
	 * 
	 * @param beanType   Bean类型
	 * @param beanValues 属性表；
	 * @param <T>        T
	 * @return T 设置了属性值的Bean实例；
	 */
	public static <T> T createBean(Class<T> beanType, Properties beanValues) {
		return createBean(beanType, beanValues, "");
	}

	/**
	 * 创建配置对象的实例，并且从指定的属性表中初始化对应的实例字段；
	 * 
	 * @param beanType   Bean的类型；
	 * @param beanValues 属性表；
	 * @param beanPrefix 在属性表中与配置对象相关的属性名称的前缀；
	 * @param <T>        T
	 * @return T
	 */
	@SuppressWarnings("unchecked")
	public static <T> T createBean(Class<T> beanType, Properties beanValues, String beanPrefix) {
		if (beanPrefix != null) {
			beanPrefix = beanPrefix.trim();
			if (beanPrefix.length() > 0 && !beanPrefix.endsWith(PROPERTY_SEPERATOR)) {
				beanPrefix += PROPERTY_SEPERATOR;
			}
		} else {
			beanPrefix = "";
		}
		if (beanPrefix.length() > 0) {
			beanValues = PropertiesUtils.subset(beanValues, beanPrefix, true);
		}

		BeanWrapper beanWrapper = createBeanByType(beanType);
		setPropertyValues(beanValues, beanWrapper);

		return (T) beanWrapper.getWrappedInstance();
	}

	private static <T> BeanWrapper createBeanByType(Class<T> beanType) {
		ExtBeanWrapperImpl beanWrapper = new ExtBeanWrapperImpl(beanType);
		initBean(beanWrapper);
		return beanWrapper;
	}

	private static BeanWrapper createBeanByInstance(Object bean) {
		ExtBeanWrapperImpl beanWrapper = new ExtBeanWrapperImpl(bean);
		initBean(beanWrapper);
		return beanWrapper;
	}

	private static void initBean(BeanWrapperImpl beanWrapper) {

		beanWrapper.setAutoGrowNestedPaths(true);

		beanWrapper.getPropertyDescriptors();
	}

	/**
	 * 设置 Bean 的属性；
	 * 
	 * @param bean       配置对象；配置值将设置到此对象匹配的属性；
	 * @param beanValues 配置值；
	 * @param beanPrefix 自动加入的属性前缀；
	 */
	public static void setValues(Object bean, Properties beanValues, String beanPrefix) {
		Properties values = new Properties();
		PropertiesUtils.mergeFrom(values, beanValues, beanPrefix);
		setValues(bean, values);
	}

	/**
	 * 设置 Bean 的属性；
	 * 
	 * @param bean       配置对象；读取属性表的值用于更新此对象中对应的属性；
	 * @param beanValues 属性表；
	 */
	public static void setValues(Object bean, Properties beanValues) {
		BeanWrapper beanWrapper = createBeanByInstance(bean);

		setPropertyValues(beanValues, beanWrapper);
	}

	private static void setPropertyValues(Properties beanValues, BeanWrapper bean) {
		MutablePropertyValues values = new MutablePropertyValues(beanValues);
		bean.setPropertyValues(values, true);
	}

	/**
	 * 导出 bean 的属性表 ；
	 * 
	 * @param <T>
	 * @param bean     Bean实例；
	 * @param beanType Bean类型；只导出指定类型的属性值；
	 * @return 包含要导出的bean属性值的属性表；
	 */
	public static <T> Properties exportValues(T bean, Class<T> beanType) {
		Properties props = new SortedProperties();
		exportValues(bean, beanType, "", props);
		return props;
	}

	/**
	 * 导出 bean 的属性表 ；
	 * 
	 * @param <T>
	 * @param bean     Bean实例；
	 * @param beanType Bean类型；只导出指定类型的属性值；
	 * @return 包含要导出的bean属性值的属性表；
	 */
	public static <T> Properties exportValues(T bean, Class<T> beanType, boolean includedAllInterfaces) {
		Properties props = new SortedProperties();
		exportValues(bean, beanType, includedAllInterfaces, "", props);
		return props;
	}

	/**
	 * 导出 bean 的属性表 ；
	 * 
	 * @param <T>
	 * @param bean     Bean实例；
	 * @param beanType Bean类型；只导出指定类型的属性值；
	 * @param prefix   属性名的前缀；
	 * @return 包含要导出的bean属性值的属性表；
	 */
	public static <T> Properties exportValues(T bean, Class<T> beanType, String prefix) {
		Properties props = new SortedProperties();
		exportValues(bean, beanType, prefix, props);
		return props;
	}

	/**
	 * 导出 bean 的属性表 ；
	 * 
	 * @param <T>        属性类型；
	 * @param bean       Bean实例；
	 * @param beanType   Bean类型；只导出指定类型的属性值；
	 * @param properties 包含要导出的bean属性值的属性表；
	 */
	public static <T> void exportValues(T bean, Class<T> beanType, Properties properties) {
		exportValues(bean, beanType, "", properties);
	}

	/**
	 * 导出 bean 的属性表 ；
	 * 
	 * @param <T>        属性类型；
	 * @param bean       Bean实例；
	 * @param beanType   Bean类型；只导出指定类型的属性值；
	 * @param properties 包含要导出的bean属性值的属性表；
	 */
	public static <T> void exportValues(T bean, Class<T> beanType, boolean includedAllInterfaces,
			Properties properties) {
		exportValues(bean, beanType, includedAllInterfaces, "", properties);
	}

	/**
	 * 导出 bean 的属性表 ；
	 * 
	 * @param <T>
	 * @param bean      Bean实例；
	 * @param beanClass Bean类型；只导出指定类型的属性值； 可以指定接口类型；
	 * @param prefix    属性名的前缀；
	 * @param values    包含要导出的bean属性值的属性表；
	 */
	public static <T> void exportValues(T bean, Class<T> beanClass, String prefix, Properties values) {
		exportValues(bean, beanClass, false, prefix, values);
	}

	/**
	 * 导出 bean 的属性表 ；
	 * 
	 * @param <T>
	 * @param bean                  Bean实例；
	 * @param beanClass             Bean类型；只导出指定类型的属性值； 可以指定接口类型；
	 * @param includedAllInterfaces 是否包含全部接口；当指定的 beanClass 是接口类型时，此参数才有效，此时，如果参数为
	 *                              true ，则会解析接口类型 implements 声明的所有接口；
	 * @param prefix                属性名的前缀；
	 * @param values                包含要导出的bean属性值的属性表；
	 */
	public static <T> void exportValues(T bean, Class<T> beanClass, boolean includedAllInterfaces, String prefix,
			Properties values) {
		prefix = prefix.trim();
		// 去掉前缀中可能存在最后位置的属性分隔符；
		if (prefix.length() > 0 && prefix.charAt(prefix.length() - 1) == PROPERTY_SEPERATOR_CHAR) {
			int i = prefix.length() - 1;
			for (; i > -1; i--) {
				if (prefix.charAt(i) != PROPERTY_SEPERATOR_CHAR) {
					break;
				}
			}
			prefix = prefix.substring(0, i + 1);
		}

		// --------------------------------------------------------------------------------------------------
		// 递归地解析要导出指定的Bean类型的属性清单；
		// 无论是当前指定的类型，还是属性的类型，都仅针对声明类型做导出；
		// 注：这是与 BeanWrapperImpl 实现的不同之处，而且，由于 BeanWrapperImpl 实现严格封装了内部实现，也无法继承其实现进行扩展；
		// --------------------------------------------------------------------------------------------------

		// 输出的属性表；
		Properties properties = new SortedProperties();

		// 要进行递归处理的 Bean 堆栈；
		Deque<BeanProcessHolder> beanStacks = new LinkedList<BeanUtils.BeanProcessHolder>();
		try {
			// 首先将顶层 Bean 入栈；
			beanStacks.offer(new BeanProcessHolder(new BeanType(beanClass, includedAllInterfaces), prefix, bean));

			BeanProcessHolder beanContext = null;
			while ((beanContext = beanStacks.pollFirst()) != null) {
				BeanType beanType = beanContext.getBeanType();
				Set<PropertyDescriptor> beanPropDescriptors = beanType.getPropertyDescriptors();
				for (PropertyDescriptor propertyDescriptor : beanPropDescriptors) {
					if (propertyDescriptor.getReadMethod() == null) {
						// 忽略不可读方法；
						continue;
					}
					if ("class".equals(propertyDescriptor.getName())) {
						// 忽略 getClass() 属性；
						continue;
					}
					propertyDescriptor.getReadMethod().setAccessible(true);
					Object propertyValue = propertyDescriptor.getReadMethod().invoke(beanContext.bean);

					// 检查是否指定了自定义转换器；
					@SuppressWarnings("rawtypes")
					StringConverter converter = BeanConverterRegistry.getPropertyConverter(propertyDescriptor);
					if (converter != null) {
						// 指定了转换器的属性看作是简单类型；
						String propName = beanContext.formatPropertyName(propertyDescriptor.getName());
						@SuppressWarnings("unchecked")
						String textValue = converter.encode(propertyValue);
						if (textValue != null) {
							properties.setProperty(propName, textValue);
						}
						continue;
					}

					if (propertyValue == null) {
						continue;
					}
					Class<?> propClass = propertyDescriptor.getPropertyType();
					if (org.springframework.beans.BeanUtils.isSimpleProperty(propClass)) {
						// simple value;
						if (propClass.isArray()) {
							int len = Array.getLength(propertyValue);
							for (int i = 0; i < len; i++) {
								Object propItemValue = Array.get(propertyValue, i);
								String propItemName = beanContext.formatArrayItemName(propertyDescriptor.getName(), i);
								String itemTextValue = propItemValue == null ? "" : propItemValue.toString();
								properties.setProperty(propItemName, itemTextValue);
							}
						} else {
							String propName = beanContext.formatPropertyName(propertyDescriptor.getName());
							String textValue = propertyValue == null ? "" : propertyValue.toString();

							properties.setProperty(propName, textValue);
						}
					} else if (Map.class.isAssignableFrom(propClass)) {
						// map 类型；
						Map<?, ?> mapValue = (Map<?, ?>) propertyValue;
						String propNameBase = beanContext.formatPropertyName(propertyDescriptor.getName());
						for (Map.Entry<?, ?> entries : mapValue.entrySet()) {
							String propName = String.format("%s[%s]", propNameBase, entries.getKey().toString());
							Object entryValue = entries.getValue();
							// TODO: 未处理值为 Bean 对象的情形；
							String textValue = entryValue == null ? "" : entryValue.toString();

							properties.setProperty(propName, textValue);
						}
					} else {
						// Bean value;
						if (propClass.isArray()) {
							int len = Array.getLength(propertyValue);
							Class<?> propertyBeanClass = propClass.getComponentType();
							BeanType propertyBeanType = new BeanType(propertyBeanClass, includedAllInterfaces);
							for (int i = 0; i < len; i++) {
								Object propItemBean = Array.get(propertyValue, i);
								String propItemPrefix = beanContext.formatArrayItemName(propertyDescriptor.getName(),
										i);
								BeanProcessHolder propItemBeanCtx = new BeanProcessHolder(propertyBeanType,
										propItemPrefix, propItemBean);

								// 将下层数组属性的 Bean 入栈；
								beanStacks.offer(propItemBeanCtx);
							}
						} else {
							// Bean;
							Class<?> propertyBeanClass = propClass;
							BeanType propertyBeanType = new BeanType(propertyBeanClass, includedAllInterfaces);
							String propPrefix = beanContext.formatPropertyName(propertyDescriptor.getName());
							BeanProcessHolder propBeanCtx = new BeanProcessHolder(propertyBeanType, propPrefix,
									propertyValue);
							beanStacks.offer(propBeanCtx);
						}
					}
				} // End of : for;
			} // End of : while;
		} catch (ArrayIndexOutOfBoundsException | IllegalAccessException | IllegalArgumentException
				| InvocationTargetException e) {
			throw new IllegalStateException(e.getMessage(), e);
		}

		PropertiesUtils.copy(properties, values);
	}

	public static <T> void store(T bean, Class<T> beanType, File file) {
		Properties properties = exportValues(bean, beanType);
		FileUtils.writeProperties(properties, null, file);
	}

	public static <T> void store(T bean, Class<T> beanType, File file, String charset) {
		Properties properties = exportValues(bean, beanType);
		FileUtils.writeProperties(properties, null, file, charset);
	}

	/**
	 * 从指定的路径加载配置；
	 * 
	 * @param beanType 配置对象的类型；
	 * @param file     文件的路径；可以指定资源路径表达式；
	 * @param <T>      class
	 * @return T
	 * @throws IOException exception
	 */
	public static <T> T load(Class<T> beanType, String file) {
		Properties props = FileUtils.readProperties(file);
		return createBean(beanType, props);
	}

	/**
	 * 从指定的路径加载配置；
	 * 
	 * @param beanType 配置对象的类型；
	 * @param file     文件的路径；可以指定资源路径表达式；
	 * @param <T>      class
	 * @return T
	 * @throws IOException exception
	 */
	public static <T> T load(Class<T> beanType, File file) {
		Properties props = FileUtils.readProperties(file);
		return createBean(beanType, props);
	}

	/**
	 * 从指定的路径加载配置；
	 * 
	 * @param beanType 配置对象的类型；
	 * @param file     文件的路径；可以指定资源路径表达式；
	 * @param charset  字符集；
	 * @param <T>      class
	 * @return T
	 * @throws IOException exception
	 */
	public static <T> T load(Class<T> beanType, String file, String charset) {
		Properties props = FileUtils.readProperties(file, charset);
		return createBean(beanType, props);
	}

	/**
	 * 从指定的路径加载配置；
	 * 
	 * @param obj     配置对象；配置文件的值将设置到此对象匹配的属性；
	 * @param file    文件的路径；可以指定资源路径表达式；
	 * @param charset 字符集；
	 * @throws IOException exception
	 */
	public static void load(Object obj, String file, String charset) {
		Properties props = FileUtils.readProperties(file, charset);
		setValues(obj, props);
	}

	/**
	 * 替换为 {@link FileUtils#readProperties(String, String)} ；
	 * 
	 * @param file
	 * @param charset
	 * @return
	 * @throws IOException
	 */
	@Deprecated
	public static Properties loadProperties(String file, String charset) throws IOException {
		return FileUtils.readProperties(file, charset);
	}

	/**
	 * 替换为 {@link FileUtils#readProperties(File, String)} ；
	 * 
	 * @param file
	 * @param charset
	 * @return
	 * @throws IOException
	 */
	@Deprecated
	public static Properties loadProperties(File file, String charset) throws IOException {
		return FileUtils.readProperties(file, charset);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source 源对象；
	 * @param target 目标对象；
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Object target) {
		// org.springframework.beans.BeanUtils.copyProperties(source, target,
		// targetType);
		innerCopyProperties(source, null, target, null, false, null);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source          源对象；
	 * @param target          目标对象；
	 * @param ignoreNullValue 是否忽略来源字段的空值；默认为 false ；
	 *                        <p>
	 *                        如果设置为 true ，当来源字段的值为 null 时，不写入目标字段；
	 *                        <p>
	 *                        如果设置为 false，则来源字段的值无论是否为 null ，都覆盖目标字段的值；
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Object target, boolean ignoreNullValue) {
		// org.springframework.beans.BeanUtils.copyProperties(source, target,
		// targetType);
		innerCopyProperties(source, null, target, null, ignoreNullValue, null);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source     源对象；
	 * @param target     目标对象；
	 * @param targetType 目标类型；
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Object target, Class<?> targetType) {
		// org.springframework.beans.BeanUtils.copyProperties(source, target,
		// targetType);
		innerCopyProperties(source, null, target, targetType, false, null);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source          源对象；
	 * @param target          目标对象；
	 * @param targetType      目标类型；
	 * @param ignoreNullValue 是否忽略来源字段的空值；默认为 false ；
	 *                        <p>
	 *                        如果设置为 true ，当来源字段的值为 null 时，不写入目标字段；
	 *                        <p>
	 *                        如果设置为 false，则来源字段的值无论是否为 null ，都覆盖目标字段的值；
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Object target, Class<?> targetType, boolean ignoreNullValue) {
		// org.springframework.beans.BeanUtils.copyProperties(source, target,
		// targetType);
		innerCopyProperties(source, null, target, targetType, ignoreNullValue, null);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source       源对象；
	 * @param target       目标对象；
	 * @param targetType   目标类型；
	 * @param ignoreFields 忽略的字段；
	 * @throws BeansException
	 */

	public static void copyProperties(Object source, Class<?> sourceType, Object target, Class<?> targetType,
			@Nullable FieldSelection<?> ignoreFields) {
		Set<String> ignoreList = (ignoreFields != null ? ignoreFields.getFieldNames() : null);
		innerCopyProperties(source, sourceType, target, targetType, false, ignoreList);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source          源对象；
	 * @param target          目标对象；
	 * @param targetType      目标类型；
	 * @param ignoreNullValue 是否忽略来源字段的空值；默认为 false ；
	 *                        <p>
	 *                        如果设置为 true ，当来源字段的值为 null 时，不写入目标字段；
	 *                        <p>
	 *                        如果设置为 false，则来源字段的值无论是否为 null ，都覆盖目标字段的值；
	 * @param ignoreFields    忽略的字段；
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Class<?> sourceType, Object target, Class<?> targetType,
			boolean ignoreNullValue,
			@Nullable FieldSelection<?> ignoreFields) {
		Set<String> ignoreList = (ignoreFields != null ? ignoreFields.getFieldNames() : null);
		innerCopyProperties(source, sourceType, target, targetType, ignoreNullValue, ignoreList);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source          源对象；
	 * @param target          目标对象；
	 * @param targetType      目标类型；
	 * @param ignoreFieldList 忽略的字段；
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Class<?> sourceType, Object target, Class<?> targetType,
			@Nullable Set<String> ignoreFieldList) {
		innerCopyProperties(source, sourceType, target, targetType, false, ignoreFieldList);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source          源对象；
	 * @param target          目标对象；
	 * @param targetType      目标类型；
	 * @param ignoreNullValue 是否忽略来源字段的空值；默认为 false ；
	 *                        <p>
	 *                        如果设置为 true ，当来源字段的值为 null 时，不写入目标字段；
	 *                        <p>
	 *                        如果设置为 false，则来源字段的值无论是否为 null ，都覆盖目标字段的值；
	 * @param ignoreFieldList 忽略的字段；
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Class<?> sourceType, Object target, Class<?> targetType,
			boolean ignoreNullValue,
			@Nullable Set<String> ignoreList) {
		innerCopyProperties(source, sourceType, target, targetType, ignoreNullValue, ignoreList);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source          源对象；
	 * @param target          目标对象；
	 * @param targetType      目标类型；
	 * @param ignoreFieldList 忽略的字段；
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Class<?> sourceType, Object target, Class<?> targetType,
			@Nullable String... ignoreProperties) {
		Set<String> ignoreList = (ignoreProperties != null ? CollectionUtils.merge(ignoreProperties) : null);
		innerCopyProperties(source, sourceType, target, targetType, false, ignoreList);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source          源对象；
	 * @param target          目标对象；
	 * @param targetType      目标类型；
	 * @param ignoreNullValue 是否忽略来源字段的空值；默认为 false ；
	 *                        <p>
	 *                        如果设置为 true ，当来源字段的值为 null 时，不写入目标字段；
	 *                        <p>
	 *                        如果设置为 false，则来源字段的值无论是否为 null ，都覆盖目标字段的值；
	 * @param ignoreFieldList 忽略的字段；
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Class<?> sourceType, Object target, Class<?> targetType,
			boolean ignoreNullValue,
			@Nullable String... ignoreProperties) {
		Set<String> ignoreList = (ignoreProperties != null ? CollectionUtils.merge(ignoreProperties) : null);
		innerCopyProperties(source, sourceType, target, targetType, ignoreNullValue, ignoreList);
	}

	/**
	 * 复制属性；
	 * 
	 * @param source          源对象；
	 * @param target          目标对象；
	 * @param targetType      目标类型；
	 * @param ignoreNullValue 是否忽略来源字段的空值；默认为 false ；
	 *                        <p>
	 *                        如果设置为 true ，当来源字段的值为 null 时，不写入目标字段；
	 *                        <p>
	 *                        如果设置为 false，则来源字段的值无论是否为 null ，都覆盖目标字段的值；
	 * @param ignoreFieldList 忽略的字段；
	 * @throws BeansException
	 */
	private static void innerCopyProperties(Object source, @Nullable Class<?> sourceType, Object target,
			@Nullable Class<?> targetType, boolean ignoreNullValue,
			@Nullable Set<String> ignoreFieldList) throws BeansException {

		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualSourceType = source.getClass();
		if (sourceType != null) {
			// 如果指定了目标类型，则目标对象必须实现目标类型；
			if (!sourceType.isInstance(source)) {
				throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
						"] not assignable to Editable class [" + targetType.getName() + "]");
			}
			actualSourceType = sourceType;
		}

		Class<?> actualTargetType = target.getClass();
		if (targetType != null) {
			// 如果指定了目标类型，则目标对象必须实现目标类型；
			if (!targetType.isInstance(target)) {
				throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
						"] not assignable to Editable class [" + targetType.getName() + "]");
			}
			actualTargetType = targetType;
		}
		PropertyDescriptor[] targetPds = org.springframework.beans.BeanUtils.getPropertyDescriptors(actualTargetType);

		// 目标类型与源类型相同或是源类型的父类型；
		boolean beanTypeAssignable = actualTargetType.isAssignableFrom(actualSourceType)
				|| actualSourceType.isAssignableFrom(actualTargetType);

		// 根据目标对象的可写属性，决定复制的值；
		boolean noWriteMethod = true;
		for (PropertyDescriptor targetPd : targetPds) {
			Method writeMethod = targetPd.getWriteMethod();
			if (writeMethod == null) {
				continue;
			}

			noWriteMethod = false;
			if (ignoreFieldList == null || !ignoreFieldList.contains(targetPd.getName())) {
				// 检查源数据对象中是否有匹配的可读属性；
				PropertyDescriptor sourcePd = org.springframework.beans.BeanUtils
						.getPropertyDescriptor(actualSourceType, targetPd.getName());
				if (sourcePd != null) {
					Method readMethod = sourcePd.getReadMethod();
					if (readMethod != null) {
						// 源对象中具有匹配的可读属性；
						//
						// 如果源类型和目标类型是继承关系，或者相同，
						// 则对于共有的字段类型，默认为类型匹配，不再进一步校验字段类型，以提升效率；
						// 虽然这一次策略并不严谨—— Java 语法允许有继承关系的类型的字段之间，read 方法类型和 set 方法类型有可能不一致，
						// 但这种情况下，会引发异常，并不会导致字段值被错误地复制；
						boolean isAssignable = beanTypeAssignable;
						if (!isAssignable) {
							// 进一步检查源属性的返回类型是否匹配目标属性的类型；
							ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
							ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);

							// Ignore generic types in assignable check if either ResolvableType has
							// unresolvable generics.
							isAssignable = (sourceResolvableType.hasUnresolvableGenerics()
									|| targetResolvableType.hasUnresolvableGenerics()
											? ClassUtils.isAssignable(writeMethod.getParameterTypes()[0],
													readMethod.getReturnType())
											: targetResolvableType.isAssignableFrom(sourceResolvableType));
						}

						if (isAssignable) {
							try {
								if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
									readMethod.setAccessible(true);
								}
								Object value = readMethod.invoke(source);
								if (ignoreNullValue && value == null) {
									// 忽略空值；
									continue;
								}
								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
									writeMethod.setAccessible(true);
								}
								writeMethod.invoke(target, value);
							} catch (Throwable ex) {
								throw new FatalBeanException(
										"Could not copy property '" + targetPd.getName() + "' from source to target",
										ex);
							}
						}
					}
				}
			}
		}

		if (noWriteMethod) {
			throw new ApplicationException("The target type["+actualTargetType.getName()+"] has no any writable method!");
		}
	}

	/**
	 * 解析并返回整个属性路径中首个嵌套属性路径的元素；
	 * <p>
	 * 例如：<br>
	 * 对于属性路径 propertyName1[key1][key2].propertyName2[key3].propertyName3 ，将解析第一部分
	 * propertyName1[key1][key2] 并返回；
	 * 
	 * @param propertyPath
	 * @return
	 */
	public static PropertyTokens getFirstNestedPropertyTokens(String propertyPath) {
		int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
		// Handle nested properties recursively.
		if (pos > -1) {
			String nestedProperty = propertyPath.substring(0, pos);
			// String nestedPath = propertyPath.substring(pos + 1);
			return resolvePropertyNameTokens(nestedProperty);
		}
		return resolvePropertyNameTokens(propertyPath);
	}

	/**
	 * 返回首个嵌套的属性名；
	 * <p>
	 * 等效于 {@link #getFirstNestedPropertyTokens(String)} 返回结果的 actualName
	 * {@link PropertyTokens#getActualName()} ；
	 * <p>
	 * 例如：<br>
	 * 对于属性路径 propertyName1[key1][key2].propertyName2[key3].propertyName3 ，将返回
	 * propertyName1；
	 * 
	 * @param propertyPath
	 * @return
	 */
	public static String getFirstNestedPropertyName(String propertyPath) {
		String firstNestedProperty = propertyPath;
		int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
		if (pos > -1) {
			firstNestedProperty = propertyPath.substring(0, pos);
		}
		int idx = firstNestedProperty.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX);
		if (idx > -1) {
			return firstNestedProperty.substring(0, idx);
		}
		return firstNestedProperty;
	}

	/**
	 * Parse the given property name into the corresponding property name tokens.
	 * 
	 * 
	 * @param propertyName 要转换的属性名；形式为 actualName[key1][key2];
	 * @return representation of the parsed property tokens
	 */
	private static PropertyTokens resolvePropertyNameTokens(String propertyName) {
		String propertyPath = propertyName.trim();
		String actualName = null;
		List<String> keys = new ArrayList<>(2);
		int searchIndex = 0;
		while (searchIndex != -1) {
			int keyStart = propertyPath.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX, searchIndex);
			searchIndex = -1;
			if (keyStart != -1) {
				int keyEnd = getPropertyNameKeyEnd(propertyPath,
						keyStart + PropertyAccessor.PROPERTY_KEY_PREFIX.length());
				if (keyEnd != -1) {
					if (actualName == null) {
						actualName = propertyPath.substring(0, keyStart);
					}
					String key = propertyPath.substring(keyStart + PropertyAccessor.PROPERTY_KEY_PREFIX.length(),
							keyEnd);
					if (key.length() > 1 && (key.startsWith("'") && key.endsWith("'"))
							|| (key.startsWith("\"") && key.endsWith("\""))) {
						key = key.substring(1, key.length() - 1);
					}
					keys.add(key);
					searchIndex = keyEnd + PropertyAccessor.PROPERTY_KEY_SUFFIX.length();
				}
			}
		}
		PropertyTokens tokens = new PropertyTokens(actualName != null ? actualName : propertyPath);
		if (!keys.isEmpty()) {
			tokens.keys = Collections.unmodifiableList(keys);
		}
		return tokens;
	}

	public static String generateCanonicalKeyPath(String key) {
		return PropertyAccessor.PROPERTY_KEY_PREFIX + key + PropertyAccessor.PROPERTY_KEY_SUFFIX;
	}

	public static String generateCanonicalKeyPath(List<String> keys) {
		return PropertyAccessor.PROPERTY_KEY_PREFIX
				+ StringUtils.collectionToDelimitedString(keys,
						PropertyAccessor.PROPERTY_KEY_SUFFIX + PropertyAccessor.PROPERTY_KEY_PREFIX)
				+ PropertyAccessor.PROPERTY_KEY_SUFFIX;
	}

	private static int getPropertyNameKeyEnd(String propertyName, int startIndex) {
		int unclosedPrefixes = 0;
		int length = propertyName.length();
		for (int i = startIndex; i < length; i++) {
			switch (propertyName.charAt(i)) {
				case PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR:
					// The property name contains opening prefix(es)...
					unclosedPrefixes++;
					break;
				case PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR:
					if (unclosedPrefixes == 0) {
						// No unclosed prefix(es) in the property name (left) ->
						// this is the suffix we are looking for.
						return i;
					} else {
						// This suffix does not close the initial prefix but rather
						// just one that occurred within the property name.
						unclosedPrefixes--;
					}
					break;
			}
		}
		return -1;
	}

	/**
	 * Holder class used to store property tokens.
	 * 
	 * <p>
	 * 
	 */
	public static class PropertyTokens {

		public PropertyTokens(String name) {
			this.actualName = name;
		}

		private String actualName;

		@Nullable
		private List<String> keys;

		public String getActualName() {
			return actualName;
		}

		public String getCanonicalName() {
			return actualName + getCanonicalKeysPath();
		}

		public List<String> getKeys() {
			return keys;
		}

		public String getCanonicalKeysPath() {
			return generateCanonicalKeyPath(keys);
		}

	}

	private static class BeanProcessHolder {

		private final String propertyPrefix;

		private Object bean;

		private BeanType beanType;

		public BeanProcessHolder(BeanType beanType, String propertyPrefix, Object bean) {
			this.propertyPrefix = propertyPrefix;
			this.bean = bean;
			this.beanType = beanType;
		}

		/**
		 * @return the beanInfo
		 */
		public BeanType getBeanType() {
			return beanType;
		}

		public String formatPropertyName(String name) {
			if (propertyPrefix.length() > 0) {
				return String.format("%s.%s", propertyPrefix, name);
			} else {
				return name;
			}
		}

		public String formatArrayItemName(String name, int i) {
			if (propertyPrefix.length() > 0) {
				return String.format("%s.%s[%s]", propertyPrefix, name, i);
			} else {
				return String.format("%s[%s]", name, i);
			}
		}

		/**
		 * @return the prefix
		 */
		public String getPropertyPrefix() {
			return propertyPrefix;
		}

		/**
		 * @return the bean
		 */
		public Object getBean() {
			return bean;
		}

	}

}
